package cc.rboot.feign.configuration;

import cc.rboot.feign.annotation.FeignService;
import cc.rboot.feign.transcoder.FeignEncoder;
import cc.rboot.feign.transcoder.FeignDecoder;
import feign.Feign;
import feign.Request;
import feign.Retryer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.function.Supplier;

@Configuration
public class RBootFeignConfiguration implements BeanDefinitionRegistryPostProcessor, EnvironmentAware {

    private Environment environment;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        String BASE_PACKAGE = environment.getProperty("rboot.feign.base-package");
        String[] BASE_PACKAGE_SPLIT = BASE_PACKAGE.split(",");

        for (String s : BASE_PACKAGE_SPLIT) {
            this.execute(registry, s);
        }

    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    private void execute(BeanDefinitionRegistry registry, String BASE_PACKAGE){
        String RESOURCE_PATTERN = "/**/*.class";
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + RESOURCE_PATTERN;
        Resource[] resources = new Resource[0];
        try {
            resources = resourcePatternResolver.getResources(pattern);
        } catch (IOException e) {
            e.printStackTrace();
        }
        MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
        for (Resource resource : resources) {
            MetadataReader reader = null;
            try {
                reader = readerFactory.getMetadataReader(resource);
            } catch (IOException e) {
                e.printStackTrace();
            }
            String classname = reader.getClassMetadata().getClassName();
            Class<?> clazz = null;
            try {
                clazz = Class.forName(classname);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            FeignService feignService = clazz.getAnnotation(FeignService.class);
            if (null != feignService) {
                BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
                        .genericBeanDefinition(clazz, new FeignServiceSupplier(clazz));
                BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
                registry.registerBeanDefinition(clazz.getName(), beanDefinition);
            }
        }
    }

    private class FeignServiceSupplier implements Supplier {
        private Class clazz;

        public FeignServiceSupplier(Class clazz) {
            this.clazz = clazz;
        }

        @Override
        public Object get() {
            FeignService annotation = (FeignService) clazz.getAnnotation(FeignService.class);

            String url = annotation.url();
            if(url.startsWith("${")){
                String propertyKey = url.replace("${", "").replace("}", "");
                url = environment.getProperty(propertyKey);
            }

            Object feignInstance = Feign.builder()
                    .encoder(new FeignEncoder())
                    .decoder(new FeignDecoder())
                    .options(new Request.Options(2000, 3500))
                    .retryer(new Retryer.Default(5000, 5000, 3))
                    .target(clazz, url);
            return feignInstance;
        }
    }
}
